1 /*
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2002 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
53 */
54 package net.sf.cglib.proxy;
55
56 import net.sf.cglib.core.*;
57 import java.lang.reflect.Method;
58 import java.util.*;
59 import org.objectweb.asm.ClassVisitor;
60
61 /***
62 * Generates dynamic subclasses to enable method interception. This
63 * class started as a substitute for the standard Dynamic Proxy support
64 * included with JDK 1.3, but one that allowed the proxies to extend a
65 * concrete base class, in addition to implementing interfaces. The dynamically
66 * generated subclasses override the non-final methods of the superclass and
67 * have hooks which callback to user-defined interceptor
68 * implementations.
69 * <p>
70 * The original and most general callback type is the {@link MethodInterceptor}, which
71 * in AOP terms enables "around advice"--that is, you can invoke custom code both before
72 * and after the invocation of the "super" method. In addition you can modify the
73 * arguments before calling the super method, or not call it at all.
74 * <p>
75 * Although <code>MethodInterceptor</code> is generic enough to meet any
76 * interception need, it is often overkill. For simplicity and performance, additional
77 * specialized callback types, such as {@link LazyLoader} are also available.
78 * Often a single callback will be used per enhanced class, but you can control
79 * which callback is used on a per-method basis with a {@link CallbackFilter}.
80 * <p>
81 * The most common uses of this class are embodied in the static helper methods. For
82 * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
83 * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
84 * <p>
85 * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
86 * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
87 * to change the callbacks of an existing object, as well as a faster and easier way to create
88 * new instances of the same type.
89 * <p>
90 * For an almost drop-in replacement for
91 * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
92 */
93 public class Enhancer extends AbstractClassGenerator
94 {
95 private static final Source SOURCE = new Source(Enhancer.class.getName());
96 private static final EnhancerKey KEY_FACTORY =
97 (EnhancerKey)KeyFactory.create(EnhancerKey.class, KeyFactory.CLASS_BY_NAME);
98
99 interface EnhancerKey {
100 public Object newInstance(Class type,
101 Class[] interfaces,
102 CallbackFilter filter,
103 Class[] callbackTypes,
104 boolean useFactory);
105 }
106
107 private Class[] interfaces;
108 private CallbackFilter filter;
109 private Callback[] callbacks;
110 private Class[] callbackTypes;
111 private boolean classOnly;
112 private Class superclass;
113 private Class[] argumentTypes;
114 private Object[] arguments;
115 private boolean useFactory = true;
116
117 /***
118 * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
119 * object should be used for each generated object, and should not
120 * be shared across threads. To create additional instances of a
121 * generated class, use the <code>Factory</code> interface.
122 * @see Factory
123 */
124 public Enhancer() {
125 super(SOURCE);
126 }
127
128 /***
129 * Set the class which the generated class will extend. As a convenience,
130 * if the supplied superclass is actually an interface, <code>setInterfaces</code>
131 * will be called with the appropriate argument instead.
132 * A non-interface argument must not be declared as final, and must have an
133 * accessible constructor.
134 * @param superclass class to extend or interface to implement
135 * @see #setInterfaces(Class[])
136 */
137 public void setSuperclass(Class superclass) {
138 if (superclass != null && superclass.isInterface()) {
139 setInterfaces(new Class[]{ superclass });
140 } else if (superclass != null && superclass.equals(Object.class)) {
141 // affects choice of ClassLoader
142 this.superclass = null;
143 } else {
144 this.superclass = superclass;
145 }
146 }
147
148 /***
149 * Set the interfaces to implement. The <code>Factory</code> interface will
150 * always be implemented regardless of what is specified here.
151 * @param interfaces array of interfaces to implement, or null
152 * @see Factory
153 */
154 public void setInterfaces(Class[] interfaces) {
155 this.interfaces = interfaces;
156 }
157
158 /***
159 * Set the {@link CallbackFilter} used to map the generated class' methods
160 * to a particular callback index.
161 * New object instances will always use the same mapping, but may use different
162 * actual callback objects.
163 * @param filter the callback filter to use when generating a new class
164 * @see #setCallbacks
165 */
166 public void setCallbackFilter(CallbackFilter filter) {
167 this.filter = filter;
168 }
169
170 /***
171 * Set the single {@link Callback} to use.
172 * Ignored if you use {@link #createClass}.
173 * @param callback the callback to use for all methods
174 * @see #setCallbacks
175 */
176 public void setCallback(final Callback callback) {
177 setCallbacks(new Callback[]{ callback });
178 }
179
180 /***
181 * Set the array of callbacks to use.
182 * Ignored if you use {@link #createClass}.
183 * You must use a {@link CallbackFilter} to specify the index into this
184 * array for each method in the proxied class.
185 * @param callbacks the callback array
186 * @see #setCallbackFilter
187 * @see #setCallback
188 */
189 public void setCallbacks(Callback[] callbacks) {
190 if (callbacks != null && callbacks.length == 0) {
191 throw new IllegalArgumentException("Array cannot be empty");
192 }
193 this.callbacks = callbacks;
194 }
195
196 /***
197 * Set whether the enhanced object instances should implement
198 * the {@link Factory} interface.
199 * This was added for tools that need for proxies to be more
200 * indistinguishable from their targets. Also, in some cases it may
201 * be necessary to disable the <code>Factory</code> interface to
202 * prevent code from changing the underlying callbacks.
203 * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
204 */
205 public void setUseFactory(boolean useFactory) {
206 this.useFactory = useFactory;
207 }
208
209 /***
210 * Set the single type of {@link Callback} to use.
211 * This may be used instead of {@link #setCallback} when calling
212 * {@link #createClass}, since it may not be possible to have
213 * an array of actual callback instances.
214 * @param callbackType the type of callback to use for all methods
215 * @see #setCallbackTypes
216 */
217 public void setCallbackType(Class callbackType) {
218 setCallbackTypes(new Class[]{ callbackType });
219 }
220
221 /***
222 * Set the array of callback types to use.
223 * This may be used instead of {@link #setCallbacks} when calling
224 * {@link #createClass}, since it may not be possible to have
225 * an array of actual callback instances.
226 * You must use a {@link CallbackFilter} to specify the index into this
227 * array for each method in the proxied class.
228 * @param callbackTypes the array of callback types
229 */
230 public void setCallbackTypes(Class[] callbackTypes) {
231 if (callbackTypes != null && callbackTypes.length == 0) {
232 throw new IllegalArgumentException("Array cannot be empty");
233 }
234 this.callbackTypes = callbackTypes;
235 }
236
237 /***
238 * Generate a new class if necessary and uses the specified
239 * callbacks (if any) to create a new object instance.
240 * Uses the no-arg constructor of the superclass.
241 * @return a new instance
242 */
243 public Object create() {
244 classOnly = false;
245 argumentTypes = null;
246 return createHelper();
247 }
248
249 /***
250 * Generate a new class if necessary and uses the specified
251 * callbacks (if any) to create a new object instance.
252 * Uses the constructor of the superclass matching the <code>argumentTypes</code>
253 * parameter, with the given arguments.
254 * @param argumentTypes constructor signature
255 * @param arguments compatible wrapped arguments to pass to constructor
256 * @return a new instance
257 */
258 public Object create(Class[] argumentTypes, Object[] arguments) {
259 classOnly = false;
260 if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
261 throw new IllegalArgumentException("Arguments must be non-null and of equal length");
262 }
263 this.argumentTypes = argumentTypes;
264 this.arguments = arguments;
265 return createHelper();
266 }
267
268 /***
269 * Generate a new class if necessary and return it without creating a new instance.
270 * This ignores any callbacks that have been set.
271 * To create a new instance you will have to use reflection, and methods
272 * called during the constructor will not be intercepted. To avoid this problem,
273 * use the multi-arg <code>create</code> method.
274 * @see #create(Class[], Object[])
275 */
276 public Class createClass() {
277 classOnly = true;
278 return (Class)createHelper();
279 }
280
281 private Object createHelper() {
282 if (classOnly ^ (callbacks == null)) {
283 if (classOnly) {
284 throw new IllegalStateException("createClass does not accept callbacks");
285 } else {
286 throw new IllegalStateException("callbacks are required unless using createClass");
287 }
288 }
289 if (callbacks == null && callbackTypes == null) {
290 throw new IllegalStateException("Either callbacks or callback types are always required");
291 }
292 if (callbacks != null && callbackTypes != null) {
293 if (callbacks.length != callbackTypes.length) {
294 throw new IllegalStateException("Lengths of callback and callback types array must be the same");
295 }
296 for (int i = 0; i < callbacks.length; i++) {
297 // make sure all classes are callbacks
298 CallbackUtils.getGenerator(callbackTypes[i]);
299 if (callbacks[i] == null) {
300 throw new IllegalStateException("Callback cannot be null");
301 }
302 if (!callbackTypes[i].isAssignableFrom(callbacks[i].getClass())) {
303 throw new IllegalStateException("Callback " + callbacks[i] + " is not assignable to " + callbackTypes[i]);
304 }
305 }
306 } else if (callbacks != null) {
307 callbackTypes = new Class[callbacks.length];
308 for (int i = 0; i < callbacks.length; i++) {
309 callbackTypes[i] = CallbackUtils.determineType(callbacks[i]);
310 }
311 } else {
312 for (int i = 0; i < callbackTypes.length; i++) {
313 // make sure all classes are callbacks
314 CallbackUtils.getGenerator(callbackTypes[i]);
315 }
316 }
317 if (filter == null) {
318 if (callbackTypes.length > 1) {
319 throw new IllegalStateException("Multiple callback types possible but no filter specified");
320 }
321 filter = CallbackFilter.ALL_ZERO;
322 }
323
324 if (superclass != null) {
325 setNamePrefix(superclass.getName());
326 } else if (interfaces != null) {
327 setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
328 }
329 Object key = KEY_FACTORY.newInstance(superclass, interfaces, filter, callbackTypes, useFactory);
330 return super.create(key);
331 }
332
333 protected ClassLoader getDefaultClassLoader() {
334 if (superclass != null) {
335 return superclass.getClassLoader();
336 } else if (interfaces != null) {
337 return interfaces[0].getClassLoader();
338 } else {
339 return null;
340 }
341 }
342
343 public void generateClass(ClassVisitor v) throws Exception {
344 new EnhancerEmitter(v, getClassName(), superclass, interfaces, filter, callbackTypes, useFactory);
345 }
346
347 protected Object firstInstance(Class type) throws Exception {
348 if (classOnly) {
349 return type;
350 } else {
351 return createUsingReflection(type);
352 }
353 }
354
355 protected Object nextInstance(Object instance) {
356 Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
357 if (classOnly) {
358 return protoclass;
359 } else if (instance instanceof Factory) {
360 if (argumentTypes != null) {
361 return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
362 } else {
363 return ((Factory)instance).newInstance(callbacks);
364 }
365 } else {
366 return createUsingReflection(protoclass);
367 }
368 }
369
370 /***
371 * Call this method to register the {@link Callback} array to use before
372 * creating a new instance of the generated class via reflection. If you are using
373 * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
374 * new instances, this method is unnecessary. Its primary use is for when you want to
375 * cache and reuse a generated class yourself, and the generated class does
376 * <i>not</i> implement the {@link Factory} interface.
377 * @see #setUseFactory
378 */
379 public static void registerCallbacks(Class generatedClass, Callback[] callbacks) {
380 EnhancerEmitter.setThreadCallbacks(generatedClass, callbacks);
381 }
382
383 private Object createUsingReflection(Class type) {
384 EnhancerEmitter.setThreadCallbacks(type, callbacks);
385 if (argumentTypes != null) {
386 return ReflectUtils.newInstance(type, argumentTypes, arguments);
387 } else {
388 return ReflectUtils.newInstance(type);
389 }
390 }
391
392 /***
393 * Helper method to create an intercepted object.
394 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
395 * instead of this static method.
396 * @param type class to extend or interface to implement
397 * @param callback the callback to use for all methods
398 */
399 public static Object create(Class type, Callback callback) {
400 Enhancer e = new Enhancer();
401 e.setSuperclass(type);
402 e.setCallback(callback);
403 return e.create();
404 }
405
406 /***
407 * Helper method to create an intercepted object.
408 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
409 * instead of this static method.
410 * @param type class to extend or interface to implement
411 * @param interfaces array of interfaces to implement, or null
412 * @param callback the callback to use for all methods
413 */
414 public static Object create(Class superclass, Class interfaces[], Callback callback) {
415 Enhancer e = new Enhancer();
416 e.setSuperclass(superclass);
417 e.setInterfaces(interfaces);
418 e.setCallback(callback);
419 return e.create();
420 }
421
422 /***
423 * Helper method to create an intercepted object.
424 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
425 * instead of this static method.
426 * @param type class to extend or interface to implement
427 * @param interfaces array of interfaces to implement, or null
428 * @param filter the callback filter to use when generating a new class
429 * @param callbacks callback implementations to use for the enhanced object
430 */
431 public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) {
432 Enhancer e = new Enhancer();
433 e.setSuperclass(superclass);
434 e.setInterfaces(interfaces);
435 e.setCallbackFilter(filter);
436 e.setCallbacks(callbacks);
437 return e.create();
438 }
439 }
This page was automatically generated by Maven